//////////////////////////////////////////////////////////////////////////
// $Id: Effect.cpp,v 1.1 2009/04/03 12:46:11 sunjun Exp $
//////////////////////////////////////////////////////////////////////////

#include "Local.h"

//////////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////////

NAMESPACE_BEGIN

//////////////////////////////////////////////////////////////////////////

	class EffectInclude : public ID3DXInclude 
	{
	public:
		STDMETHOD(Open)(THIS_ D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes) 
		{
			FILE * file = fopen( pFileName, "rb" ); CHECK(file);
			U32 pos = ftell( file );
			fseek( file, 0, SEEK_END );
			U32 size = ftell( file );
			fseek( file, pos, SEEK_SET );
			U8 * data = MNEW(U8[size]);
			CHECK(data);			
			CHECK( fread( data, 1, size, file ) == size );
			fclose( file );	

			*pBytes = size;
			*ppData = data;

			if (*ppData && *pBytes)
				return S_OK;
			else
				return S_FALSE;
		}

		STDMETHOD(Close)(THIS_ LPCVOID pData) 
		{
			U8 * data = (U8*)pData;
			MDELETE([]data);
			return S_OK;
		}
	};

	//////////////////////////////////////////////////////////////////////////

	Effect::Effect( const Context* rc ) : mHandle(NULL) 
	{
		CHECK(rc);
		mRCPtr = (Context*)rc;
	}

	Effect::~Effect() 
	{
		if((ID3DXEffect*)mHandle){((ID3DXEffect*)mHandle)->Release(); mHandle = NULL;}
	}

	//////////////////////////////////////////////////////////////////////////

	/*
	====================
	handle
	====================
	*/
	INLINE HANDLE Effect::handle() const
	{
		return mHandle;
	}

	/*
	====================
	load
	====================
	*/
	INLINE VOID Effect::load( const VOID* data, U32 size, const CHAR** macros )
	{
		CHECK(data&&size);

		if((ID3DXEffect*)mHandle){((ID3DXEffect*)mHandle)->Release(); mHandle = NULL;}
		
		// preprocess the macros of the effect
		std::vector<D3DXMACRO> d3dxMacros;
		if( macros )
		{
			I32 i = 0;
			while( macros[i] )
			{
				D3DXMACRO d3dxMacro;
				d3dxMacro.Name = macros[i++];
				d3dxMacro.Definition = macros[i++];
				d3dxMacros.push_back(d3dxMacro);
			}
		}		
		D3DXMACRO d3dxMacro;
		d3dxMacro.Name = 0;
		d3dxMacro.Definition = 0;
		d3dxMacros.push_back(d3dxMacro);

		// create the effect
		LPD3DXBUFFER d3dxBuffer = 0;		
		EffectInclude effect_include;
		ID3DXEffect * d3dxEffect = NULL;
		HRESULT hr = ::D3DXCreateEffect(
			(IDirect3DDevice9*)mRCPtr->handle(),
			data, 
			size, 
			&d3dxMacros[0], 
			&effect_include, 
			D3DXSHADER_NO_PRESHADER, 
			0, 
			&d3dxEffect, 
			&d3dxBuffer );
		if( FAILED(hr) )
		{
			CHECK(d3dxBuffer);
			MAssert(VA("Effect::load : create the effect is failed(%s)!", d3dxBuffer->GetBufferPointer()));
		}
		mHandle = d3dxEffect;
	}

	//////////////////////////////////////////////////////////////////////////

	/*
	====================
	getParameter
	====================
	*/
	INLINE HANDLE Effect::getParameter( const CHAR* name ) const
	{
		CHECK(mHandle);
		return (HANDLE)((ID3DXEffect*)mHandle)->GetParameterByName( NULL, name );
	}

	/*
	====================
	getTechnique
	====================
	*/
	INLINE HANDLE Effect::getTechnique( const CHAR* name ) const
	{
		CHECK(mHandle);
		return (HANDLE)((ID3DXEffect*)mHandle)->GetTechniqueByName( name );
	}

	/*
	====================
	getPass
	====================
	*/
	INLINE HANDLE Effect::getPass( HANDLE technique, const CHAR* name ) const
	{
		CHECK(mHandle);
		return (HANDLE)((ID3DXEffect*)mHandle)->GetPassByName( (D3DXHANDLE)technique, name );
	}

	/*
	====================
	setValue
	====================
	*/
	INLINE VOID Effect::setValue( HANDLE handle, const VOID* data, U32 size )
	{
		CHECK(mHandle&&handle&&data);
		HRESULT hr = ((ID3DXEffect*)mHandle)->SetValue( (D3DXHANDLE)handle, data, size );
		if( FAILED(hr) ) MAssert(VA("Effect::setValue : %s!", DXGetErrorDescription(hr)));
	}

	/*
	====================
	setBool
	====================
	*/
	INLINE VOID Effect::setBool( HANDLE handle, BOOL b )
	{
		CHECK(mHandle&&handle);
		HRESULT hr = ((ID3DXEffect*)mHandle)->SetBool( (D3DXHANDLE)handle, b );
		if( FAILED(hr) ) MAssert(VA("Effect::SetBool : %s!", DXGetErrorDescription(hr)));
	}

	/*
	====================
	setBoolArray
	====================
	*/
	INLINE VOID Effect::setBoolArray( HANDLE handle, const BOOL* pb, U32 count )
	{
		CHECK(mHandle&&handle&&pb);
		HRESULT hr = ((ID3DXEffect*)mHandle)->SetBoolArray( (D3DXHANDLE)handle, (const I32*)pb, count );
		if( FAILED(hr) ) MAssert(VA("Effect::setBoolArray : %s!", DXGetErrorDescription(hr)));
	}

	/*
	====================
	setInt
	====================
	*/
	INLINE VOID Effect::setInt( HANDLE handle, I32 n )
	{
		CHECK(mHandle&&handle);
		HRESULT hr = ((ID3DXEffect*)mHandle)->SetInt( (D3DXHANDLE)handle, n );
		if( FAILED(hr) ) MAssert(VA("Effect::setInt : %s!", DXGetErrorDescription(hr)));
	}

	/*
	====================
	setIntArray
	====================
	*/
	INLINE VOID Effect::setIntArray( HANDLE handle, const I32* pn, U32 count )
	{
		CHECK(mHandle&&handle&&pn);
		HRESULT hr = ((ID3DXEffect*)mHandle)->SetIntArray( (D3DXHANDLE)handle, (const INT*)pn, count );
		if( FAILED(hr) ) MAssert(VA("Effect::setIntArray : %s!", DXGetErrorDescription(hr)));
	}

	/*
	====================
	setFloat
	====================
	*/
	INLINE VOID Effect::setFloat( HANDLE handle, F32 f )
	{
		CHECK(mHandle&&handle);
		HRESULT hr = ((ID3DXEffect*)mHandle)->SetFloat( (D3DXHANDLE)handle, f );
		if( FAILED(hr) ) MAssert(VA("Effect::setFloat : %s!", DXGetErrorDescription(hr)));
	}

	/*
	====================
	setFloatArray
	====================
	*/
	INLINE VOID Effect::setFloatArray( HANDLE handle, const F32* pf, U32 count )
	{
		CHECK(mHandle&&handle&&pf);
		HRESULT hr = ((ID3DXEffect*)mHandle)->SetFloatArray( (D3DXHANDLE)handle, (const FLOAT*)pf, count );
		if( FAILED(hr) ) MAssert(VA("Effect::setFloatArray : %s!", DXGetErrorDescription(hr)));
	}

	/*
	====================
	setVector
	====================
	*/
	INLINE VOID Effect::setVector( HANDLE handle, const Vector4& vec )
	{
		CHECK(mHandle&&handle);
		HRESULT hr = ((ID3DXEffect*)mHandle)->SetVector( (D3DXHANDLE)handle, (const D3DXVECTOR4*)vec.ptr() );
		if( FAILED(hr) ) MAssert(VA("Effect::setVector : %s!", DXGetErrorDescription(hr)));
	}

	/*
	====================
	setVectorArray
	====================
	*/
	INLINE VOID Effect::setVectorArray( HANDLE handle, const Vector4* pvec, U32 count )
	{
		CHECK(mHandle&&handle&&pvec);
		HRESULT hr = ((ID3DXEffect*)mHandle)->SetVectorArray( (D3DXHANDLE)handle, (const D3DXVECTOR4*)pvec, count );
		if( FAILED(hr) ) MAssert(VA("Effect::setVectorArray : %s!", DXGetErrorDescription(hr)));
	}

	/*
	====================
	setMatrix
	====================
	*/
	INLINE VOID Effect::setMatrix( HANDLE handle, const Matrix& mat )
	{
		CHECK(mHandle&&handle);
		HRESULT hr = ((ID3DXEffect*)mHandle)->SetMatrix( (D3DXHANDLE)handle, (const D3DXMATRIX*)mat.ptr() );
		if( FAILED(hr) ) MAssert(VA("Effect::setMatrix : %s!", DXGetErrorDescription(hr)));
	}

	/*
	====================
	setMatrixArray
	====================
	*/
	INLINE VOID Effect::setMatrixArray( HANDLE handle, const Matrix* pmat, U32 count )
	{
		CHECK(mHandle&&handle&&pmat);
		HRESULT hr = ((ID3DXEffect*)mHandle)->SetMatrixArray( (D3DXHANDLE)handle, (const D3DXMATRIX*)pmat, count );
		if( FAILED(hr) ) MAssert(VA("Effect::setMatrixArray : %s!", DXGetErrorDescription(hr)));
	}

	/*
	====================
	setString
	====================
	*/
	INLINE VOID Effect::setString( HANDLE handle, const CHAR* string )
	{
		CHECK(mHandle&&handle&&string);
		HRESULT hr = ((ID3DXEffect*)mHandle)->SetString( (D3DXHANDLE)handle, (LPCSTR)string );
		if( FAILED(hr) ) MAssert(VA("Effect::setString : %s!", DXGetErrorDescription(hr)));
	}

	/*
	====================
	setTexture
	====================
	*/
	INLINE VOID Effect::setTexture( HANDLE handle, const Texture* texture )
	{
		CHECK(mHandle&&handle&&texture);
		HRESULT hr = ((ID3DXEffect*)mHandle)->SetTexture( (D3DXHANDLE)handle, (LPDIRECT3DBASETEXTURE9)texture->handle() );
		if( FAILED(hr) ) MAssert(VA("Effect::setTexture : %s!", DXGetErrorDescription(hr)));
	}

	/*
	====================
	begin
	====================
	*/
	INLINE U32 Effect::begin( HANDLE technique )
	{
		CHECK(mHandle);

		// updates changes to any set calls in the pass.
		CHECK( SUCCEEDED(((ID3DXEffect*)mHandle)->CommitChanges()) );

		// selecting and setting a technique
		if(technique) 
		{
			HRESULT hr = ((ID3DXEffect*)mHandle)->SetTechnique( (D3DXHANDLE)technique );
			if( FAILED(hr) ) MAssert(VA("Effect::begin : %s!", DXGetErrorDescription(hr)));
		}

		// starts active technique
		UINT count;
		HRESULT hr = ((ID3DXEffect*)mHandle)->Begin( &count, D3DXFX_DONOTSAVESTATE );
		if( FAILED(hr) ) MAssert(VA("Effect::begin : %s!", DXGetErrorDescription(hr)));

		return count;
	}

	/*
	====================
	beginPass
	====================
	*/
	INLINE VOID Effect::beginPass( U32 pass )
	{
		CHECK(mHandle);
		HRESULT hr = ((ID3DXEffect*)mHandle)->BeginPass( pass );
		if( FAILED(hr) ) MAssert(VA("Effect::beginPass : %s!", DXGetErrorDescription(hr)));
	}

	/*
	====================
	endPass
	====================
	*/
	INLINE VOID Effect::endPass()
	{
		CHECK(mHandle);
		HRESULT hr = ((ID3DXEffect*)mHandle)->EndPass();
		if( FAILED(hr) ) MAssert(VA("Effect::endPass : %s!", DXGetErrorDescription(hr)));
	}

	/*
	====================
	end
	====================
	*/
	INLINE VOID Effect::end()
	{
		CHECK(mHandle);
		HRESULT hr = ((ID3DXEffect*)mHandle)->End();
		if( FAILED(hr) ) MAssert(VA("Effect::end : %s!", DXGetErrorDescription(hr)));
	}

//////////////////////////////////////////////////////////////////////////

NAMESPACE_END

//////////////////////////////////////////////////////////////////////////
